"""
主界面窗口
普通话声调评分工具的主要用户界面
"""
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib
matplotlib.use('TkAgg')

import numpy as np
import os
import json
from datetime import datetime
import threading
import time

# 导入项目模块
from audio.recorder import AudioRecorder
from audio.praat_processor import PraatProcessor
from analysis.curve_fitting import ToneCurveFitter
from analysis.dwt_calculator import DWTCalculator
from utils.audio_parser import AudioParser

class MainWindow:
    def __init__(self, root):
        self.root = root
        self.root.title("普通话声调评分工具")
        self.root.geometry("1200x800")
        
        # 初始化组件
        self.recorder = AudioRecorder()
        self.praat_processor = PraatProcessor()
        self.curve_fitter = ToneCurveFitter()
        self.dwt_calculator = DWTCalculator()
        self.audio_parser = AudioParser()
        
        # 数据存储
        self.standard_audio_info = None
        self.standard_data = None
        self.user_recordings = []
        self.current_session = None
        
        # UI状态
        self.is_recording = False
        self.recording_start_time = 0
        
        self.setup_ui()
        self.setup_callbacks()
        
    def setup_ui(self):
        """设置用户界面"""
        # 创建主框架
        main_frame = ttk.Frame(self.root)
        main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 左侧控制面板
        self.create_control_panel(main_frame)
        
        # 右侧图表区域
        self.create_chart_area(main_frame)
        
        # 底部状态栏
        self.create_status_bar(main_frame)
    
    def create_control_panel(self, parent):
        """创建左侧控制面板"""
        control_frame = ttk.Frame(parent)
        control_frame.pack(side=tk.LEFT, fill=tk.Y, padx=(0, 10))
        
        # 标准音频选择区域
        std_frame = ttk.LabelFrame(control_frame, text="标准音频", padding=10)
        std_frame.pack(fill=tk.X, pady=(0, 10))
        
        ttk.Button(std_frame, text="选择标准音频文件", 
                  command=self.select_standard_audio).pack(fill=tk.X, pady=(0, 5))
        
        self.std_info_label = ttk.Label(std_frame, text="未选择标准音频", 
                                       foreground="gray")
        self.std_info_label.pack(fill=tk.X)
        
        # 声调选择
        tone_frame = ttk.Frame(std_frame)
        tone_frame.pack(fill=tk.X, pady=(5, 0))
        ttk.Label(tone_frame, text="声调:").pack(side=tk.LEFT)
        
        self.tone_var = tk.StringVar(value="1")
        tone_combo = ttk.Combobox(tone_frame, textvariable=self.tone_var,
                                 values=["1 (一声)", "2 (二声)", "3 (三声)", "4 (四声)"],
                                 state="readonly", width=10)
        tone_combo.pack(side=tk.LEFT, padx=(5, 0))
        
        # 录音控制区域
        record_frame = ttk.LabelFrame(control_frame, text="录音控制", padding=10)
        record_frame.pack(fill=tk.X, pady=(0, 10))
        
        self.record_btn = ttk.Button(record_frame, text="开始录音", 
                                    command=self.toggle_recording)
        self.record_btn.pack(fill=tk.X, pady=(0, 5))
        
        self.record_status_label = ttk.Label(record_frame, text="就绪", 
                                           foreground="green")
        self.record_status_label.pack(fill=tk.X)
        
        # 录音时长显示
        self.duration_label = ttk.Label(record_frame, text="时长: 0.0秒")
        self.duration_label.pack(fill=tk.X)
        
        # 分析控制区域
        analysis_frame = ttk.LabelFrame(control_frame, text="分析控制", padding=10)
        analysis_frame.pack(fill=tk.X, pady=(0, 10))
        
        ttk.Button(analysis_frame, text="处理标准音频", 
                  command=self.process_standard_audio).pack(fill=tk.X, pady=(0, 3))
        
        ttk.Button(analysis_frame, text="分析当前录音", 
                  command=self.analyze_current_recording).pack(fill=tk.X, pady=(0, 3))
        
        ttk.Button(analysis_frame, text="更新图表", 
                  command=self.update_chart).pack(fill=tk.X, pady=(0, 3))
        
        # 历史记录区域
        history_frame = ttk.LabelFrame(control_frame, text="历史记录", padding=10)
        history_frame.pack(fill=tk.BOTH, expand=True)
        
        # 历史记录列表
        self.history_tree = ttk.Treeview(history_frame, 
                                        columns=("录音", "距离", "相似度"), 
                                        show="tree headings", height=8)
        
        self.history_tree.heading("#0", text="时间")
        self.history_tree.heading("录音", text="录音")
        self.history_tree.heading("距离", text="DWT距离")
        self.history_tree.heading("相似度", text="相似度")
        
        self.history_tree.column("#0", width=80)
        self.history_tree.column("录音", width=60)
        self.history_tree.column("距离", width=80)
        self.history_tree.column("相似度", width=80)
        
        self.history_tree.pack(fill=tk.BOTH, expand=True, pady=(0, 5))
        
        # 历史记录操作按钮
        history_btn_frame = ttk.Frame(history_frame)
        history_btn_frame.pack(fill=tk.X)
        
        ttk.Button(history_btn_frame, text="清空", 
                  command=self.clear_history).pack(side=tk.LEFT, padx=(0, 5))
        ttk.Button(history_btn_frame, text="保存", 
                  command=self.save_session).pack(side=tk.LEFT)
    
    def create_chart_area(self, parent):
        """创建右侧图表区域"""
        chart_frame = ttk.Frame(parent)
        chart_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)
        
        # 创建matplotlib图表
        self.fig, self.ax = plt.subplots(figsize=(8, 6))
        self.ax.set_xlabel('时间 (归一化)')
        self.ax.set_ylabel('基频 (Hz)')
        self.ax.set_title('声调基频对比')
        self.ax.grid(True, alpha=0.3)
        
        # 嵌入到tkinter中
        self.canvas = FigureCanvasTkAgg(self.fig, chart_frame)
        self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
        
        # 初始化空图表
        self.ax.plot([], [], 'b-', linewidth=2, label='标准曲线')
        self.ax.legend()
        self.canvas.draw()
    
    def create_status_bar(self, parent):
        """创建底部状态栏"""
        status_frame = ttk.Frame(parent)
        status_frame.pack(side=tk.BOTTOM, fill=tk.X, pady=(10, 0))
        
        self.status_label = ttk.Label(status_frame, text="就绪")
        self.status_label.pack(side=tk.LEFT)
        
        # 进度条
        self.progress = ttk.Progressbar(status_frame, mode='indeterminate')
        self.progress.pack(side=tk.RIGHT, padx=(10, 0))
    
    def setup_callbacks(self):
        """设置回调函数"""
        # 录音回调
        self.recorder.on_recording_start = self.on_recording_start
        self.recorder.on_recording_stop = self.on_recording_stop
        self.recorder.on_recording_update = self.on_recording_update
    
    def update_status(self, message):
        """更新状态栏"""
        self.status_label.config(text=message)
        self.root.update_idletasks()
    
    def start_progress(self):
        """开始进度指示"""
        self.progress.start()
    
    def stop_progress(self):
        """停止进度指示"""
        self.progress.stop()
    
    def select_standard_audio(self):
        """选择标准音频文件"""
        file_path = filedialog.askopenfilename(
            title="选择标准音频文件",
            filetypes=[("WAV文件", "*.wav"), ("所有文件", "*.*")]
        )
        
        if file_path:
            # 解析文件名
            filename = os.path.basename(file_path)
            parsed_info = self.audio_parser.parse_filename(filename)
            
            if parsed_info:
                self.standard_audio_info = parsed_info
                self.standard_audio_info['full_path'] = file_path
                
                # 更新显示
                info_text = f"{parsed_info['character']}({parsed_info['pinyin_marked']}) {parsed_info['tone_name']}"
                self.std_info_label.config(text=info_text, foreground="black")
                
                # 更新声调选择
                self.tone_var.set(str(parsed_info['tone']))
                
                self.update_status(f"已选择标准音频: {info_text}")
            else:
                messagebox.showwarning("警告", "无法解析音频文件名格式")
    
    def toggle_recording(self):
        """切换录音状态"""
        if not self.is_recording:
            self.start_recording()
        else:
            self.stop_recording()
    
    def start_recording(self):
        """开始录音"""
        if not self.standard_audio_info:
            messagebox.showwarning("警告", "请先选择标准音频文件")
            return
        
        self.is_recording = True
        self.record_btn.config(text="停止录音")
        self.recorder.start_recording(max_duration=10.0)
    
    def stop_recording(self):
        """停止录音"""
        self.is_recording = False
        self.record_btn.config(text="开始录音")
        self.recorder.stop_recording()
    
    def on_recording_start(self):
        """录音开始回调"""
        self.recording_start_time = time.time()
        self.record_status_label.config(text="录音中...", foreground="red")
        self.update_status("正在录音...")
    
    def on_recording_stop(self, duration):
        """录音停止回调"""
        self.record_status_label.config(text="录音完成", foreground="green")
        self.duration_label.config(text=f"时长: {duration:.1f}秒")
        self.update_status(f"录音完成，时长: {duration:.1f}秒")
    
    def on_recording_update(self, duration, blocks):
        """录音更新回调"""
        self.duration_label.config(text=f"时长: {duration:.1f}秒")
    
    def process_standard_audio(self):
        """处理标准音频"""
        if not self.standard_audio_info:
            messagebox.showwarning("警告", "请先选择标准音频文件")
            return
        
        def process_worker():
            try:
                self.start_progress()
                self.update_status("正在提取标准音频基频...")
                
                # 提取基频
                audio_path = self.standard_audio_info['full_path']
                time_array, f0_array = self.praat_processor.extract_f0_contour(audio_path)
                
                if len(f0_array) == 0:
                    self.update_status("提取基频失败")
                    messagebox.showerror("错误", "无法提取标准音频的基频")
                    return
                
                self.update_status("正在拟合标准曲线...")
                
                # 曲线拟合
                tone = int(self.tone_var.get().split()[0])
                fitting_result = self.curve_fitter.fit_tone_curve(time_array, f0_array, tone)
                
                if not fitting_result['success']:
                    self.update_status("曲线拟合失败")
                    messagebox.showerror("错误", "标准音频曲线拟合失败")
                    return
                
                # 生成采样点
                sample_points = self.curve_fitter.generate_sample_points(fitting_result, 20)
                
                # 保存标准数据
                self.standard_data = {
                    'audio_info': self.standard_audio_info,
                    'time_array': time_array,
                    'f0_array': f0_array,
                    'fitting_result': fitting_result,
                    'sample_points': sample_points
                }
                
                self.update_status("标准音频处理完成")
                
                # 更新图表
                self.root.after(0, self.update_chart)
                
            except Exception as e:
                self.update_status(f"处理失败: {str(e)}")
                messagebox.showerror("错误", f"处理标准音频时发生错误: {str(e)}")
            finally:
                self.stop_progress()
        
        # 在后台线程中处理
        threading.Thread(target=process_worker, daemon=True).start()
    
    def analyze_current_recording(self):
        """分析当前录音"""
        if not self.standard_data:
            messagebox.showwarning("警告", "请先处理标准音频")
            return
        
        audio_data = self.recorder.get_recorded_audio()
        if audio_data is None:
            messagebox.showwarning("警告", "没有可分析的录音数据")
            return
        
        def analyze_worker():
            try:
                self.start_progress()
                self.update_status("正在保存录音文件...")
                
                # 保存录音到临时文件
                temp_path = os.path.join("temp_audio", "user_record.wav")
                if not self.recorder.save_recording(temp_path, audio_data):
                    self.update_status("保存录音失败")
                    return
                
                self.update_status("正在提取用户录音基频...")
                
                # 提取基频
                time_array, f0_array = self.praat_processor.extract_f0_contour(temp_path)
                
                if len(f0_array) == 0:
                    self.update_status("提取用户录音基频失败")
                    messagebox.showerror("错误", "无法提取用户录音的基频")
                    return
                
                self.update_status("正在拟合用户曲线...")
                
                # 曲线拟合
                tone = int(self.tone_var.get().split()[0])
                fitting_result = self.curve_fitter.fit_tone_curve(time_array, f0_array, tone)
                
                if not fitting_result['success']:
                    self.update_status("用户曲线拟合失败")
                    messagebox.showerror("错误", "用户录音曲线拟合失败")
                    return
                
                # 生成采样点
                sample_points = self.curve_fitter.generate_sample_points(fitting_result, 20)
                
                self.update_status("正在计算DWT距离...")
                
                # 计算DWT距离
                dwt_result = self.dwt_calculator.dwt_distance(
                    self.standard_data['sample_points'], 
                    sample_points
                )
                
                if not dwt_result['success']:
                    self.update_status("DWT距离计算失败")
                    messagebox.showerror("错误", "DWT距离计算失败")
                    return
                
                # 计算相似度
                similarity = self.dwt_calculator.calculate_similarity_score(dwt_result['distance'])
                
                # 保存用户录音数据
                user_data = {
                    'timestamp': datetime.now().strftime("%H:%M:%S"),
                    'time_array': time_array,
                    'f0_array': f0_array,
                    'fitting_result': fitting_result,
                    'sample_points': sample_points,
                    'dwt_result': dwt_result,
                    'similarity': similarity
                }
                
                self.user_recordings.append(user_data)
                
                # 更新历史记录列表
                self.root.after(0, lambda: self.update_history_list(user_data))
                
                self.update_status(f"分析完成 - DWT距离: {dwt_result['distance']:.3f}, 相似度: {similarity:.1f}%")
                
                # 更新图表
                self.root.after(0, self.update_chart)
                
            except Exception as e:
                self.update_status(f"分析失败: {str(e)}")
                messagebox.showerror("错误", f"分析录音时发生错误: {str(e)}")
            finally:
                self.stop_progress()
        
        # 在后台线程中分析
        threading.Thread(target=analyze_worker, daemon=True).start()
    
    def update_history_list(self, user_data):
        """更新历史记录列表"""
        record_id = len(self.user_recordings)
        self.history_tree.insert("", "end", 
                                text=user_data['timestamp'],
                                values=(f"录音{record_id}", 
                                       f"{user_data['dwt_result']['distance']:.3f}",
                                       f"{user_data['similarity']:.1f}%"))
    
    def update_chart(self):
        """更新图表"""
        self.ax.clear()
        self.ax.set_xlabel('时间 (归一化)')
        self.ax.set_ylabel('基频 (Hz)')
        self.ax.set_title('声调基频对比')
        self.ax.grid(True, alpha=0.3)
        
        # 绘制标准曲线
        if self.standard_data:
            std_time = self.standard_data['fitting_result']['time_normalized']
            std_f0 = self.standard_data['fitting_result']['fitted_values']
            self.ax.plot(std_time, std_f0, 'b-', linewidth=3, label='标准曲线', alpha=0.8)
        
        # 绘制用户录音曲线
        colors = ['red', 'green', 'orange', 'purple', 'brown', 'pink', 'gray', 'olive']
        for i, user_data in enumerate(self.user_recordings):
            color = colors[i % len(colors)]
            user_time = user_data['fitting_result']['time_normalized']
            user_f0 = user_data['fitting_result']['fitted_values']
            
            label = f"录音{i+1} (DWT:{user_data['dwt_result']['distance']:.2f})"
            self.ax.plot(user_time, user_f0, color=color, linewidth=2, 
                        label=label, alpha=0.7)
        
        self.ax.legend()
        self.canvas.draw()
    
    def clear_history(self):
        """清空历史记录"""
        if messagebox.askyesno("确认", "确定要清空所有历史记录吗？"):
            self.user_recordings.clear()
            for item in self.history_tree.get_children():
                self.history_tree.delete(item)
            self.update_chart()
            self.update_status("历史记录已清空")
    
    def save_session(self):
        """保存会话数据"""
        if not self.standard_data or not self.user_recordings:
            messagebox.showwarning("警告", "没有可保存的数据")
            return
        
        try:
            # 准备会话数据
            session_data = {
                'timestamp': datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                'character': self.standard_data['audio_info']['character'],
                'tone': int(self.tone_var.get().split()[0]),
                'standard_curve': {
                    'frequency_data': self.standard_data['f0_array'].tolist(),
                    'fitting_params': self.standard_data['fitting_result']['parameters'],
                    'sample_points': self.standard_data['sample_points'].tolist()
                },
                'user_records': []
            }
            
            for i, user_data in enumerate(self.user_recordings):
                record = {
                    'record_id': i + 1,
                    'frequency_data': user_data['f0_array'].tolist(),
                    'fitting_params': user_data['fitting_result']['parameters'],
                    'sample_points': user_data['sample_points'].tolist(),
                    'dwt_distance': user_data['dwt_result']['distance'],
                    'similarity': user_data['similarity']
                }
                session_data['user_records'].append(record)
            
            # 读取现有会话数据
            sessions_file = os.path.join("history", "sessions.json")
            if os.path.exists(sessions_file):
                with open(sessions_file, 'r', encoding='utf-8') as f:
                    data = json.load(f)
            else:
                data = {'sessions': []}
            
            # 添加新会话
            data['sessions'].append(session_data)
            
            # 保存到文件
            with open(sessions_file, 'w', encoding='utf-8') as f:
                json.dump(data, f, ensure_ascii=False, indent=2)
            
            self.update_status(f"会话已保存 - {len(self.user_recordings)}条录音记录")
            messagebox.showinfo("成功", f"会话数据已保存到 {sessions_file}")
            
        except Exception as e:
            messagebox.showerror("错误", f"保存会话时发生错误: {str(e)}")

# 使用示例
if __name__ == "__main__":
    root = tk.Tk()
    app = MainWindow(root)
    root.mainloop()
